package com.gitee.wsl.net.client.ssl

import com.gitee.wsl.net.client.CustomCertManagerImpl
import java.io.IOException
import java.io.InputStream
import java.security.*
import java.security.cert.CertificateException
import java.security.cert.CertificateFactory
import javax.net.ssl.*


/**
 * author : Android 轮子哥
 * github : https://github.com/getActivity/EasyHttp
 * time   : 2020/11/30
 * desc   : Https 证书校验工厂
 */
object HttpSslFactory {
    /**
     * 生成信任任何证书的配置
     */
    fun generateSslConfig(): HttpSslConfig {
        return generateSslConfigBase(null, null, null)
    }

    /**
     * https 单向认证
     */
    fun generateSslConfig(trustManager: X509TrustManager?): HttpSslConfig {
        return generateSslConfigBase(trustManager, null, null)
    }

    /**
     * https 单向认证
     */
    fun generateSslConfig( certificates:  List<InputStream>?): HttpSslConfig {
        return generateSslConfigBase(null, null, null, certificates)
    }

    /**
     * https 双向认证
     */
    fun generateSslConfig(
        bksFile: InputStream?,
        password: String?,
        certificates:  List<InputStream>?
    ): HttpSslConfig {
        return generateSslConfigBase(null, bksFile, password, certificates)
    }

    /**
     * https 双向认证
     */
    fun generateSslConfig(
        bksFile: InputStream?,
        password: String?,
        trustManager: X509TrustManager?
    ): HttpSslConfig {
        return generateSslConfigBase(trustManager, bksFile, password)
    }

    /**
     * 生成认证配置
     *
     * @param trustManager          可以额外配置信任服务端的证书策略，否则默认是按CA证书去验证的，若不是CA可信任的证书，则无法通过验证
     * @param bksFile               客户端使用 bks 证书校验服务端证书
     * @param password              客户端的 bks 证书密码
     * @param certificates          用含有服务端公钥的证书校验服务端证书
     */
    private fun generateSslConfigBase(
        trustManager: X509TrustManager?,
        bksFile: InputStream?,
        password: String?,
        certificates:  List<InputStream>?=null
    ): HttpSslConfig {
        return try {
            val keyManagers = prepareKeyManager(bksFile, password)
            val trustManagers = prepareTrustManager(certificates)
            val manager: X509TrustManager?
            if (trustManager != null) {
                // 优先使用用户自定义的 TrustManager
                manager = trustManager
            } else if (trustManagers != null) {
                // 然后使用默认的 TrustManager
                manager = chooseTrustManager(trustManagers)
            } else {
                // 否则使用不安全的 TrustManager
                manager = CustomCertManagerImpl()
            }
            // 创建 TLS 类型的 SsLContext 对象，使用我们的 TrustManager
            val sslContext = SSLContext.getInstance("TLS")
            // 用上面得到的 TrustManagers 初始化 SsLContext，这样 SslContext 就会信任keyStore中的证书
            // 第一个参数是授权的密钥管理器，用来授权验证，比如授权自签名的证书验证。第二个是被授权的证书管理器，用来验证服务器端的证书
            sslContext.init(keyManagers, arrayOf<TrustManager?>(manager), null)
            // 通过 SslContext 获取 SSLSocketFactory 对象
            HttpSslConfig(sslContext.socketFactory, manager)
        } catch (e: NoSuchAlgorithmException) {
            throw AssertionError(e)
        } catch (e: KeyManagementException) {
            throw AssertionError(e)
        }
    }

    private fun prepareKeyManager(bksFile: InputStream?, password: String?): Array<KeyManager>? {
        return try {
            if (bksFile == null || password == null) {
                return null
            }
            val keyStore = KeyStore.getInstance("BKS")
            keyStore.load(bksFile, password.toCharArray())
            val factory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
            factory.init(keyStore, password.toCharArray())
            factory.keyManagers
        } catch (e: IOException) {
            e.printStackTrace()
            null
        } catch (e: CertificateException) {
            e.printStackTrace()
            null
        } catch (e: UnrecoverableKeyException) {
            e.printStackTrace()
            null
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
            null
        } catch (e: KeyStoreException) {
            e.printStackTrace()
            null
        }
    }

    private fun prepareTrustManager(certificates: List<InputStream>?=null): Array<TrustManager>? {
        return if (certificates == null || certificates.isEmpty()) {
            null
        } else try {
            val certificateFactory = CertificateFactory.getInstance("X.509")
            // 创建一个默认类型的 KeyStore，存储我们信任的证书
            val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
            keyStore.load(null)
            var index = 0
            for (certStream in certificates) {
                val certificateAlias = Integer.toString(index++)
                // 证书工厂根据证书文件的流生成证书 Cert
                val cert = certificateFactory.generateCertificate(certStream)
                // 将 Cert 作为可信证书放入到 KeyStore 中
                keyStore.setCertificateEntry(certificateAlias, cert)
                try {
                    certStream?.close()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
            // 我们创建一个默认类型的 TrustManagerFactory
            val factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
            // 用我们之前的 KeyStore 实例初始化 TrustManagerFactory，这样 tmf 就会信任 KeyStore 中的证书
            factory.init(keyStore)
            // 通过 tmf 获取 TrustManager 数组，TrustManager 也会信任 KeyStore 中的证书
            factory.trustManagers
        } catch (e: IOException) {
            e.printStackTrace()
            null
        } catch (e: CertificateException) {
            e.printStackTrace()
            null
        } catch (e: KeyStoreException) {
            e.printStackTrace()
            null
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
            null
        }
    }

    private fun chooseTrustManager(trustManagers: Array<TrustManager>): X509TrustManager? {
        for (trustManager in trustManagers) {
            if (trustManager is X509TrustManager) {
                return trustManager
            }
        }
        return null
    }

    fun generateUnSafeHostnameVerifier(): HostnameVerifier {
        return UnSafeHostnameVerifier()
    }
}